package com.p2p.auth.service;

import com.p2p.auth.config.RedisPool;
import org.springframework.stereotype.Component;
import redis.clients.jedis.*;

import java.util.LinkedList;
import java.util.List;

/**
 * Title:RedisService Description: Redis 緩存類說明 Company:HK
 *
 * @author Sam
 * @date 2016年2月24日下午2:12:24
 */

@Component
public class RedisService {

    // jedis池
    private static JedisPool pool;
    // shardedJedis池
    private static ShardedJedisPool shardPool;

    // 静态代码初始化池配置
    static {
        // 创建jedis池配置实例
        JedisPoolConfig config = new JedisPoolConfig();

        int maxActivity = Integer.valueOf(RedisPool.poolMaxActive);
        int maxIdle = Integer.valueOf(RedisPool.poolMaxIdle);
        long maxWait = Long.valueOf(RedisPool.poolMaxWait);

        // 设置最大连接数
        config.setMaxTotal(maxActivity);
        // 设置最大空闲数
        config.setMaxIdle(maxIdle);
        // 设置超时时间
        config.setMaxWaitMillis(maxWait);

        // 根据配置实例化jedis池
        pool = new JedisPool(config, RedisPool.ip1,
                Integer.valueOf(RedisPool.port));

        // 创建多个redis共享服务
        JedisShardInfo jedisShardInfo1 = new JedisShardInfo(
                RedisPool.ip2, Integer.valueOf(RedisPool.port));

        JedisShardInfo jedisShardInfo2 = new JedisShardInfo(
                RedisPool.ip3, Integer.valueOf(RedisPool.port));

        List<JedisShardInfo> list = new LinkedList<JedisShardInfo>();
        list.add(jedisShardInfo1);
        list.add(jedisShardInfo2);

        // 根据配置文件,创建shared池实例
        shardPool = new ShardedJedisPool(config, list);
    }

    /**
     * 执行器,的辅助类， 它保证在执行操作之后释放数据源returnResource(jedis)
     *
     * @param <T>
     * @version V1.0
     */
    abstract static class Executor<T> {

        ShardedJedis jedis;

        ShardedJedisPool shardedJedisPool;

        public Executor(ShardedJedisPool shardedJedisPool) {
            this.shardedJedisPool = shardedJedisPool;
            jedis = this.shardedJedisPool.getResource();
        }

        /**
         * 回调
         *
         * @return 执行结果
         */
        abstract T execute();

        /**
         * 调用{@link #execute()}并返回执行结果 它保证在执行{@link #execute()}
         * 之后释放数据源returnResource(jedis)
         *
         * @return 执行结果
         */
        public T getResult() {
            T result = null;
            try {
                result = execute();
            } catch (Throwable e) {
                throw new RuntimeException("Redis execute exception", e);
            } finally {
                if (jedis != null) {
                    shardedJedisPool.returnResource(jedis);
                }
            }
            return result;
        }
    }

    /**
     * 向缓存中设置字符串内容
     *
     * @param key   key
     * @param value value
     * @return
     * @throws Exception
     */
    public static Boolean setString(final String key, final String value) {
        return new Executor<Boolean>(shardPool) {
            @Override
            Boolean execute() {
                jedis.set(key, value);
                return true;
            }

        }.getResult();
    }

    /**
     * 将值 value 关联到 key ，并将 key 的生存时间设为 expire (以秒为单位)。 如果 key 已经存在， 将覆写旧值。
     * 类似于以下两个命令: SET key value EXPIRE key expire # 设置生存时间
     * 不同之处是这个方法是一个原子性(atomic)操作，关联值和设置生存时间两个动作会在同一时间内完成，在 Redis 用作缓存时，非常实用。
     * 时间复杂度：O(1)
     *
     * @param key    key
     * @param value  string value
     * @param expire 生命周期
     * @return 设置成功时返回 OK 。当 expire 参数不合法时，返回一个错误。
     */
    public static String setString(final String key, final String value,
                                   final int expire) {
        return new Executor<String>(shardPool) {
            @Override
            String execute() {
                return jedis.setex(key, expire, value);
            }
        }.getResult();
    }

    /**
     * 返回 key 所关联的字符串值。如果 key 不存在那么返回特殊值 nil 。 假如 key 储存的值不是字符串类型，返回一个错误，因为
     * getString 只能用于处理字符串值。 时间复杂度: O(1)
     *
     * @param key key
     * @return 当 key 不存在时，返回 nil ，否则，返回 key 的值。如果 key 不是字符串类型，那么返回一个错误。
     */
    public static String getString(final String key) {
        return new Executor<String>(shardPool) {

            @Override
            String execute() {
                return jedis.get(key);
            }
        }.getResult();
    }

    /**
     * 查询是否存在这个[String] ＫＥＹ
     *
     * @param key
     * @return
     */
    public static Boolean existsKey(final String key) {
        return new Executor<Boolean>(shardPool) {
            @Override
            Boolean execute() {
                return jedis.exists(key);
            }
        }.getResult();
    }

    /**
     * @param key
     * @return byte[]
     */
    public static Boolean existsKey(final byte[] key) {
        return new Executor<Boolean>(shardPool) {
            @Override
            Boolean execute() {
                return jedis.exists(key);
            }
        }.getResult();
    }

    public static String setByte(final String key, final byte[] value) {
        return new Executor<String>(shardPool) {
            @Override
            String execute() {
                jedis.set(key.getBytes(), value);
                return key;
            }
        }.getResult();
    }

    public static byte[] getByte(final String key) {
        return new Executor<byte[]>(shardPool) {
            @Override
            byte[] execute() {
                return jedis.get(key.getBytes());
            }
        }.getResult();
    }

    public static Long hashSet(final String key, final String filed,
                               final String value) {
        return new Executor<Long>(shardPool) {
            @Override
            Long execute() {
                return jedis.hset(key, filed, value);
            }
        }.getResult();
    }

    public static Long listSet(final String key, final String value) {
        return new Executor<Long>(shardPool) {
            @Override
            Long execute() {
                return jedis.lpush(key, value);
            }
        }.getResult();
    }

    public static List<String> listGet(final String key, final long start,
                                       final long end) {
        return new Executor<List<String>>(shardPool) {
            @Override
            List<String> execute() {
                return jedis.lrange(key, start, end);
            }
        }.getResult();
    }

    public static String hashGet(final String key, final String filed) {
        return new Executor<String>(shardPool) {
            @Override
            String execute() {
                return jedis.hget(key, filed);
            }
        }.getResult();
    }

    public static Long delKey(final String key) {
        return new Executor<Long>(shardPool) {
            @Override
            Long execute() {
                return jedis.del(key);
            }
        }.getResult();
    }

    /**
     * @param key   需要删除的List key
     * @param count 移除等于value的元素，当count>0时，从表头开始查找，移除count个；当count=0时，从表头开始查找，
     *              移除所有等于value的；当count<0时，从表尾开始查找，移除|count| 个
     * @param value 删除的value
     * @return
     */
    public static Long delListVal(final String key, int count, String value) {
        return new Executor<Long>(shardPool) {
            @Override
            Long execute() {
                return jedis.lrem(key, count, value);
            }
        }.getResult();
    }


}
